// 模板元编程 (Template Metaprogramming)
// 模板元编程是C++中一种强大的编译期编程技术，它利用模板系统在编译时执行计算和生成代码，而不是在运行时。
// 这种技术允许我们将一些计算从运行时转移到编译时，从而提高程序的性能和类型安全性。

// 模板元编程的基本概念
// 1. 编译期计算
// 模板元编程的核心是在编译期执行计算，而不是运行时：
// 编译期计算阶乘
template <int N>
struct Factorial
{
    static constexpr int value = N * Factorial<N - 1>::value;
};

// 基本情况（递归终止条件）
// 全特化
template <>
struct Factorial<0>
{
    static constexpr int value = 1;
};

// 使用
constexpr int result = Factorial<5>::value; // 编译期计算 5! = 120

// 2. 类型计算
// 除了值计算，模板元编程还可以操作和转换类型：
// 移除类型的const限定符
template <typename T>
struct RemoveConst
{
    using type = T;
};

template <typename T>
struct RemoveConst<const T>
{
    using type = T;
};

// 使用
using Type1 = RemoveConst<const int>::type; // int
using Type2 = RemoveConst<int>::type;       // int

// 模板元编程的关键技术
// 1. 模板特化
// 通过特化模板来为特定情况提供不同的实现：
// 主模板
template <typename T>
struct IsPointer
{
    static constexpr bool value = false;
};

// 特化版本
template <typename T>
struct IsPointer<T *>
{
    static constexpr bool value = true;
};

// 2. 递归模板实例化
// 利用递归来实现循环逻辑：
// 计算编译期幂运算
// 非类型参数模板，理论上尖括号内可以有任意个模板参数
template <int Base, int Exponent>
struct Power
{
    static constexpr int value = Base * Power<Base, Exponent - 1>::value;
};

// 偏特化：只特化了Exponent参数
template <int Base>
struct Power<Base, 0>
{
    static constexpr int value = 1;
};

// 3. SFINAE (Substitution Failure Is Not An Error)
// 利用模板替换失败不是错误的原则来选择性地启用或禁用模板：
// 检查类型是否有size()方法
template <typename T>
struct HasSizeMethod
{
private:
    // 使用尾置返回类型语法 (->)
    // std::declval<C>() 创建一个类型 C 的虚拟实例，无需实际构造
    // decltype(std::declval<C>().size(), std::true_type{}) 尝试调用 size() 方法
    // 逗号表达式 , 的结果是最后一个表达式的类型，即 std::true_type
    // 如果 C 有 size() 方法，返回类型是 std::true_type
    // 如果 C 没有 size() 方法，这个函数模板实例化失败（但不是错误！）
    // std::true_type{} 使用花括号 {} 创建了一个 std::true_type 类型的临时对象。这是 C++ 的列表初始化语法，用于创建一个默认构造的对象。

    template <typename C>
    static auto test(int) -> decltype(std::declval<C>().size(), std::true_type{});

    // 这是一个后备函数，当第一个函数失败时被选择
    // 返回 std::false_type

    template <typename C>
    static std::false_type test(...);

public:
    // 调用 test<T>(0)，传入整数 0 优先匹配第一个 test 函数
    // decltype(...) 获取表达式的类型（std::true_type 或 std::false_type）
    // ::value 获取该类型的 value 静态成员（true 或 false）

    static constexpr bool value = decltype(test<T>(0))::value;
};

// 4. 类型特性 (Type Traits)
// 利用类型特性库来检查和转换类型：
#include <type_traits>

template <typename T>
void process(T value)
{
    if constexpr (std::is_integral_v<T>)
    {
        // 整数类型的处理
    }
    else if constexpr (std::is_floating_point_v<T>)
    {
        // 浮点类型的处理
    }
    else
    {
        // 其他类型的处理
    }
}

// 模板元编程的实际应用
// 1. 编译期断言
// 确保某些条件在编译时满足：
template <bool Condition, typename T = void>
using EnableIf = typename std::enable_if<Condition, T>::type;

template <typename T>
EnableIf<sizeof(T) >= 4> checkSize(T)
{
    // 只有当T的大小至少为4字节时，此函数才会被实例化
}

// 2. 策略选择
// 在编译时根据类型特性选择最优算法：
template <typename Iterator>
void sort(Iterator begin, Iterator end)
{
    if constexpr (std::is_same_v<
                      typename std::iterator_traits<Iterator>::iterator_category,
                      std::random_access_iterator_tag>)
    {
        // 使用快速排序
        quickSort(begin, end);
    }
    else
    {
        // 使用归并排序
        mergeSort(begin, end);
    }
}

// 3. 代码生成
// 自动生成重复性代码：
// 生成元组访问函数
template <typename Tuple, size_t... Indices>
void printTuple(const Tuple &t, std::index_sequence<Indices...>)
{
    // 这使用了C++17的折叠表达式（fold expression）来展开操作。它的作用是：
    // 对每个索引 Indices，获取元组中对应位置的元素 std::get<Indices>(t)
    // 打印元素，并在元素之间添加逗号和空格（除了第一个元素前面不加逗号）
    // 使用逗号运算符 , 将多个表达式连接起来

    ((std::cout << (Indices == 0 ? "" : ", ") << std::get<Indices>(t)), ...);
}

template <typename... Args>
void print(const std::tuple<Args...> &t)
{
    // std::index_sequence<Indices...>：一个编译期整数序列，表示元组的索引
    // 关键部分是 std::index_sequence_for<Args...>{}，它生成一个从0到sizeof...(Args)-1的整数序列。例如，如果 Args... 是 int, double, std::string，则生成 std::index_sequence<0, 1, 2>。
    std::cout << "(";
    printTuple(t, std::index_sequence_for<Args...>{});
    std::cout << ")" << std::endl;
}
// 工作原理示例
// 假设我们有以下代码：
std::tuple<int, double, std::string> t(42, 3.14, "hello");
print(t);
// 执行过程：
// 1.print(t) 被调用
// 2.打印左括号 (
// 3.调用 printTuple(t, std::index_sequence<0, 1, 2>{})
// 4.在 printTuple 中，折叠表达式展开为：
(std::cout << (0 == 0 ? "" : ", ") << std::get<0>(t)),
    (std::cout << (1 == 0 ? "" : ", ") << std::get<1>(t)),
    (std::cout << (2 == 0 ? "" : ", ") << std::get<2>(t))
        // 这等价于：
        std::cout
        << "" << 42;
std::cout << ", " << 3.14;
std::cout << ", " << "hello";

// 模板元编程的优缺点
// 优点
// 1.性能优化：将计算从运行时转移到编译时
// 2.类型安全：在编译时捕获类型错误
// 3.代码生成：自动生成重复性代码
// 4.零运行时开销：编译期计算不产生运行时开销

// 缺点
// 1.编译时间增加：复杂的模板元程序可能显著增加编译时间
// 2.代码复杂性：模板元程序通常难以理解和维护
// 3.错误信息晦涩：模板错误信息往往难以理解
// 4.调试困难：编译期代码难以调试

// 现代C++中的改进
// C++11及以后的版本引入了许多简化模板元编程的特性：
// 1.可变参数模板：处理任意数量的模板参数
// 2.类型别名模板：简化类型计算
// 3.constexpr函数：提供更自然的编译期计算方式
// 4.if constexpr：简化编译期条件分支
// 5.折叠表达式：简化参数包操作

// C++20进一步引入了概念(Concepts)和约束(Constraints)，使模板元编程更加直观和强大。

// 模板元编程是C++中一种强大但复杂的技术，掌握它可以让你编写更高效、更灵活的代码，但也需要谨慎使用，避免过度复杂化代码。